<?php

namespace Hyperbolaa\Wechatpay\Lib;

use SimpleXMLElement;

/**
 * Class XML.
 */
class XML
{
    /**
     * XML to array.
     *
     * @param string $xml XML string
     *
     * @return array|\SimpleXMLElement
     */
    public static function parse($xml)
    {
	    $backup = libxml_disable_entity_loader(true);
        $return = self::normalize(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NOBLANKS));
	    libxml_disable_entity_loader($backup);
	    return $return;
    }



	/**
	 * XML encode.
	 *
	 * @param mixed  $data
	 * @param string $root
	 * @param string $item
	 * @param string $attr
	 * @param string $id
	 *
	 * @return string
	 */
	public static function build(
		$data,
		$root = 'xml',
		$item = 'item',
		$attr = '',
		$id = 'id'
	) {
		if (is_array($attr)) {
			$_attr = [];

			foreach ($attr as $key => $value) {
				$_attr[] = "{$key}=\"{$value}\"";
			}

			$attr = implode(' ', $_attr);
		}

		$attr = trim($attr);
		$attr = empty($attr) ? '' : " {$attr}";
		$xml = "<{$root}{$attr}>";
		$xml .= self::data2Xml($data, $item, $id);
		$xml .= "</{$root}>";

		return $xml;
	}

	/**
	 * Build CDATA.
	 *
	 * @param string $string
	 *
	 * @return string
	 */
	public static function cdata($string)
	{
		return sprintf('<![CDATA[%s]]>', $string);
	}

	/**
	 * Object to array.
	 *
	 *
	 * @param SimpleXMLElement $obj
	 *
	 * @return array
	 */
	protected static function normalize($obj)
	{
		$result = null;

		if (is_object($obj)) {
			$obj = (array) $obj;
		}

		if (is_array($obj)) {
			foreach ($obj as $key => $value) {
				$res = self::normalize($value);
				if (($key === '@attributes') && ($key)) {
					$result = $res;
				} else {
					$result[$key] = $res;
				}
			}
		} else {
			$result = $obj;
		}

		return $result;
	}

	/**
	 * Array to XML.
	 *
	 * @param array  $data
	 * @param string $item
	 * @param string $id
	 *
	 * @return string
	 */
	protected static function data2Xml($data, $item = 'item', $id = 'id')
	{
		$xml = $attr = '';

		foreach ($data as $key => $val) {
			if (is_numeric($key)) {
				$id && $attr = " {$id}=\"{$key}\"";
				$key = $item;
			}

			$xml .= "<{$key}{$attr}>";

			if ((is_array($val) || is_object($val))) {
				$xml .= self::data2Xml((array) $val, $item, $id);
			} else {
				$xml .= is_numeric($val) ? $val : self::cdata($val);
			}

			$xml .= "</{$key}>";
		}

		return $xml;
	}

}
